package com.alnex.es.service;

import com.alnex.es.vo.HouseData;
import com.alnex.es.vo.SearchResult;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 优化搜索
 */
@Service
public class SearchService {

	@Autowired
	private ElasticsearchTemplate elasticsearchTemplate;

	public static final Integer ROWS = 10;

	public SearchResult search(String keyWord, Integer page) {
		//设置分页参数
		PageRequest pageRequest = PageRequest.of(page - 1, ROWS);

		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(QueryBuilders.multiMatchQuery(keyWord,"title.pinyin").operator(Operator.AND)) // match查询
				.withPageable(pageRequest)
				.withHighlightFields(new HighlightBuilder.Field("title")) // 设置高亮
				.build();

		AggregatedPage<HouseData> housePage =
				this.elasticsearchTemplate.queryForPage(searchQuery,
						HouseData.class, new SearchResultMapper() {

							@Override
							public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
								List<T> list = new ArrayList<>();
								if (response.getHits().totalHits == 0) {
									return new AggregatedPageImpl<T>(Collections.emptyList(), pageable, 0L);
								}

								for (SearchHit hit : response.getHits()) {
									// 通过反射写入数据到对象中
									T obj = (T) ReflectUtils.newInstance(clazz);

									try {
										// 写入id
										FieldUtils.writeField(obj, "id", hit.getId(), true);
									} catch (IllegalAccessException e) {
										e.printStackTrace();
									}

									// 非高亮字段的数据写入
									for (Map.Entry<String, Object> entry : hit.getSourceAsMap().entrySet()) {

										Field field = FieldUtils.getField(clazz, entry.getKey(), true);
										if (field == null) {
											continue;
										}

										try {
											FieldUtils.writeField(obj, entry.getKey(), entry.getValue(), true);
										} catch (IllegalAccessException e) {
											e.printStackTrace();
										}

										/*
										 * 处理高亮
										 */
										for (Map.Entry<String, HighlightField> stringHighlightFieldEntry :
												hit.getHighlightFields().entrySet()) {
											Text[] fragments = stringHighlightFieldEntry.getValue().fragments();

											StringBuilder sb = new StringBuilder();
											for (Text fragment : fragments) {
												sb.append(fragment.toString());
											}

											try {
												FieldUtils.writeField(obj, entry.getKey(), sb.toString(),true);
											} catch (IllegalAccessException e) {
												e.printStackTrace();
											}
										}
										list.add(obj);
									}
									return new AggregatedPageImpl<>(list, pageable, response.getHits().totalHits);
								}
								return null;
							}
						});
		return new SearchResult(housePage.getTotalPages(), housePage.getContent(), null);
	}
}